using Gateway.Common.Enums;
using Gateway.Common.Models;
using Gateway.Common.Utils;
using Gateway.Domain.Entities;
using Gateway.IApplication.Menu;
using Gateway.IApplication.Menu.Input;
using Gateway.IRepositories;
using Microsoft.EntityFrameworkCore;
using SqlSugar;

namespace Gateway.Application;
/// <summary>
/// 
/// </summary>
public class MenuApp : IMenuApp
{
    private readonly IAppRepository<Menu> _repository;
    private readonly IUnitOfWork _unitOfWork;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="repository"></param>
    /// <param name="unitOfWork"></param>
    public MenuApp(IAppRepository<Menu> repository, IUnitOfWork unitOfWork)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<string> Add(CreateMenuInput input)
    {
        var parent= await _repository.GetAsync(input.ParentId);
        var levelCode = await CreateLevelCode(input.ParentId,parent?.LevelCode);
        var entity = new Menu(name: input.Name, path: input.Path, title: input.Title, icon: input.Icon,
            type: MenuTypeEnum.FromValue(input.Type),
            component: input.Component, tag: input.Tag, hidden: input.Hidden, affix: input.Affix,
            parentId: input.ParentId,
            seqNo: input.SeqNo,
            active: input.Active,
            color: input.Color,
            fullPage: input.FullPage,
            redirect: input.Redirect,
            hiddenBreadcrumb: input.HiddenBreadcrumb,
            levelCode: levelCode
        );
        // 构建 level code 
        // 一级 00000001
        // 二级 0000000100000001
        // 三级 000000010000000100000001

        // 一级 00000002
        // 二级 0000000200000001
        // 三级 000000020000000100000001
        if (input.Apis?.Count > 0)
        {
            entity.SetApiAuths(input.Apis.Select(p => new MenuApiAuth(p.Code, p.Url)).ToList());
        }

        await _repository.AddAsync(entity);
        await _unitOfWork.CommitAsync();
        return entity.Id;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> Update(UpdateMenuInput input)
    {
        var entity = await _repository.Entities.Include(x => x.ApiAuths)
            .FirstOrDefaultAsync(p => p.Id.Equals(input.MenuId));
        if (entity == null)
            throw new MyException("数据无效");
        if (string.IsNullOrEmpty(entity.LevelCode) || entity.LevelCode.Equals(LevelCodeUtil.LevelRootCode))
        {
           var parent= await _repository.GetAsync(input.ParentId);
            entity.LevelCode = await CreateLevelCode(entity.ParentId,parent?.LevelCode??"");
        }

        entity.Update(name: input.Name, path: input.Path, title: input.Title, icon: input.Icon,
            type: MenuTypeEnum.FromValue(input.Type),
            component: input.Component, tag: input.Tag, hidden: input.Hidden, affix: input.Affix,
            parentId: input.ParentId,
            seqNo: input.SeqNo,
            active: input.Active,
            color: input.Color,
            fullPage: input.FullPage,
            redirect: input.Redirect,
            hiddenBreadcrumb: input.HiddenBreadcrumb, levelCode: entity.LevelCode);
        if (input.Apis?.Count > 0)
        {
            entity.SetApiAuths(input.Apis.Select(p => new MenuApiAuth(p.Code, p.Url)).ToList());
        }

        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> Remove(string id)
    {
        var entity = await _repository.FirstOrDefaultAsync(p => p.Id.Equals(id));
        if (entity == null)
            throw new MyException("数据无效");
        entity.SetDeleted();
        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }

    /// <summary>
    /// 改变level code 
    /// </summary>
    /// <param name="draggingNodeId">拖拽对象 的id</param>
    /// <param name="dropNodeId">释放对象 的 id</param>
    /// <param name="dropType">拖拽类型 inner  after before</param>
    /// <returns></returns>
    public async Task<bool> ChangeLevel(string draggingNodeId, string dropNodeId, string dropType)
    {
        // 获取拖拽对象信息
        var draggingNode = await _repository.GetAsync(draggingNodeId);
        string draggingLevelCode = draggingNode.LevelCode;
        var dropNode = await _repository.GetAsync(dropNodeId);
        string dropNodeLevelCode = dropNode.LevelCode;
        // inner  类型的
        if (dropType.Equals("inner"))
        {
            // 先变 dragging 
            draggingNode.SetParentId(dropNode.Id);
            draggingNode.LevelCode = await CreateLevelCode(dropNode.Id,dropNode.LevelCode);
            var childrens = await BuildChildren(draggingLevelCode, draggingNode.LevelCode);
            childrens.Add(draggingNode);
            childrens = childrens.Distinct().ToList();
            await _repository.BulkUpdate(childrens);
        }

        // after 类型的
        if (dropType.Equals("after"))
        {
            // 先变 dragging 
            var nextLevelCode = LevelCodeUtil.GetNextLevelCode(dropNode.LevelCode);
            draggingNode.LevelCode = nextLevelCode;
            draggingNode.SetParentId(dropNode.ParentId);
            var childrens = await BuildChildren(draggingLevelCode, draggingNode.LevelCode);
            childrens.Add(draggingNode);
            
            // 再变兄弟节点
            // 先查找 目标节点的 下面的 对象集合    
            var nextSiblings = await GetNextSibling(dropNode.ParentId, dropNode.LevelCode);
            if (nextSiblings.Count > 0)
            {
                foreach (var item in nextSiblings)
                {
                    string tmpLevelCode = item.LevelCode;
                    var newLevelCode = LevelCodeUtil.GetNextLevelCode(tmpLevelCode);
                    item.LevelCode = newLevelCode;
                    childrens.Add(item);
                    var tmpChildren = await BuildChildren(tmpLevelCode,newLevelCode);
                    childrens.AddRange(tmpChildren);
                }
            }

            childrens = childrens.Distinct().ToList();
            await _repository.BulkUpdate(childrens);
        }
        // before 类型的
        if (dropType.Equals("before"))
        {
            // 先变 dragging 
            draggingNode.LevelCode = dropNodeLevelCode;
            draggingNode.SetParentId(dropNode.ParentId);
            
            var childrens =await BuildChildren(draggingLevelCode, draggingNode.LevelCode) ;
            childrens.Add(draggingNode);
            
            // 再变 drop
            var nextLevelCode = LevelCodeUtil.GetNextLevelCode(dropNode.LevelCode);
            dropNode.LevelCode = nextLevelCode;
            
            childrens.AddRange(await BuildChildren(dropNodeLevelCode, dropNode.LevelCode));
            childrens.Add(dropNode);
           
            // 再变兄弟节点
            // 先查找 目标节点的 下面的 对象集合    
            var nextSiblings = await GetNextSibling(dropNode.ParentId,dropNodeLevelCode);
            if (nextSiblings.Count > 0)
            {
                foreach (var item in nextSiblings)
                {
                    string tmpLevelCode = item.LevelCode;
                    var newLevelCode = LevelCodeUtil.GetNextLevelCode(tmpLevelCode);
                    item.LevelCode = newLevelCode;
                    childrens.Add(item);
                    var tmpChildren = await BuildChildren(tmpLevelCode,newLevelCode);
                    childrens.AddRange(tmpChildren);
                }
            }
            childrens = childrens.Distinct().ToList();
            await _repository.BulkUpdate(childrens);
        }

        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="levelCode"></param>
    /// <param name="newLevelCode"></param>
    /// <returns></returns>
    private async Task<List<Menu>> BuildChildren(string levelCode,string newLevelCode)
    {
        var draggingNodeChildren = await GetChildrenByLevelCode(levelCode);
        List<Menu> result = new List<Menu>();
        if (draggingNodeChildren.Count > 0)
        {
            foreach (var item in draggingNodeChildren)
            {
                // 更新  level code 
                item.LevelCode =
                    LevelCodeUtil.ReplaceLevelCode(item.LevelCode, levelCode, newLevelCode);
                result.Add(item);
            }
        }
        return result;
    }

    /// <summary>
    /// 创建 level code 根据层级
    /// </summary>
    /// <param name="parentId">父级标识</param>
    /// <param name="parentLevelCode">父级 level code</param>
    /// <returns></returns>
    private async Task<string> CreateLevelCode(string parentId,string parentLevelCode)
    {
        if (string.IsNullOrEmpty(parentId))
        {
            parentId = "";
        }

        var levelCode = await _repository.Entities
            .Where(x => x.ParentId.Equals(parentId) && x.Deleted == BooleanStatusEnum.No).MaxAsync(x => x.LevelCode);
        if (string.IsNullOrEmpty(levelCode))
            levelCode = LevelCodeUtil.ConcatLevelCode(parentLevelCode,LevelCodeUtil.LevelRootCode);
        return LevelCodeUtil.GetNextLevelCode(levelCode);
    }

    /// <summary>
    /// 获取最大的level code 根据层级
    /// </summary>
    /// <param name="parentId">父级标识</param>
    /// <returns></returns>
    private async Task<string> GetMaxLevelCode(string parentId)
    {
        if (string.IsNullOrEmpty(parentId))
        {
            parentId = "";
        }

        var levelCode = await _repository.Entities
            .Where(x => x.ParentId.Equals(parentId) && x.Deleted == BooleanStatusEnum.No).MaxAsync(x => x.LevelCode);
        if (string.IsNullOrEmpty(levelCode))
            return LevelCodeUtil.LevelRootCode;
        return levelCode;
    }


    /// <summary>
    /// 根据 父级 levelCode 获取 子级 
    /// </summary>
    /// <param name="levelCode">父级 levelCode </param>
    /// <returns></returns>
    private async Task<List<Menu>> GetChildrenByLevelCode(string levelCode)
    {
        return await _repository.GetListAsync(x => x.LevelCode.StartsWith(levelCode) && x.LevelCode!=levelCode);
    }

    /// <summary>
    /// 获取下面的兄弟节点
    /// </summary>
    /// <param name="parentId"></param>
    /// <param name="levelCode"></param>
    /// <returns></returns>
    private async Task<List<Menu>> GetNextSibling(string parentId, string levelCode)
    {
        return await _repository.Entities.Where(x => x.ParentId.Equals(parentId)
                                                     && String.Compare(x.LevelCode, levelCode) > 0
        ).ToListAsync();
    }
}